package com.cpedia.android.deal;

import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.util.Config;
import android.util.Log;

import java.util.HashSet;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import com.cpedia.android.deal.model.Store;
import com.cpedia.android.deal.model.DealMessage;
import com.cpedia.android.deal.model.Flag;

/**
 * User: pchen
 * Date: Jun 4, 2009
 */
public class DealController implements Runnable {

    private static final String PENDING_COMMAND_TRASH =
            "com.cpedia.android.deal.DealController.trash";
    private static final String PENDING_COMMAND_MARK_READ =
            "com.cpedia.android.deal.DealController.markRead";

    private static DealController inst = null;
    private BlockingQueue<Command> mCommands = new LinkedBlockingQueue<Command>();
    private Thread mThread;
    private HashSet<DealListener> mListeners = new HashSet<DealListener>();
    private boolean mBusy;
    private Application mApplication;

    protected DealController(Application application) {
        mApplication = application;
        mThread = new Thread(this);
        mThread.start();
    }

    /**
     * Gets or creates the singleton instance of MessagingController. Application is used to
     * provide a Context to classes that need it.
     *
     * @param application
     * @return
     */
    public synchronized static DealController getInstance(Application application) {
        if (inst == null) {
            inst = new DealController(application);
        }
        return inst;
    }

    /**
     * Inject a mock controller.  Used only for testing.  Affects future calls to getInstance().
     */
    public static void injectMockController(DealController mockController) {
        inst = mockController;
    }

    public boolean isBusy() {
        return mBusy;
    }

    public void run() {
        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            try {
                Command command = mCommands.take();
                if (command.listener == null || mListeners.contains(command.listener)) {
                    mBusy = true;
                    command.runnable.run();
                    for (DealListener l : mListeners) {
                        l.controllerCommandCompleted(mCommands.size() > 0);
                    }
                }
            }
            catch (Exception e) {
                if (Config.LOGD) {
                    Log.d(Deal.LOG_TAG, "Error running command", e);
                }
            }
            mBusy = false;
        }
    }

    private void put(String description, DealListener listener, Runnable runnable) {
        try {
            Command command = new Command();
            command.listener = listener;
            command.runnable = runnable;
            command.description = description;
            mCommands.put(command);
        }
        catch (InterruptedException ie) {
            throw new Error(ie);
        }
    }

    public void addListener(DealListener listener) {
        mListeners.add(listener);
    }

    public void removeListener(DealListener listener) {
        mListeners.remove(listener);
    }

    public void checkDeal(final Context context, final DealListener listener) {
        for (DealListener l : mListeners) {
            l.checkDealStarted(context);
        }
        put("checkDeal", listener, new Runnable() {
            public void run() {
                //todo: do some checking.
                for (DealListener l : mListeners) {
                    l.checkDealFinished(context);
                }
            }
        });
    }

    public void listLocalDeals(final Account account, DealListener listener) {
        for (DealListener l : mListeners) {
            l.listLocalDealsStarted(account);
        }

        try {
            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
            DealMessage[] localMessages = localStore.getDealMessages();
            ArrayList<DealMessage> messages = new ArrayList<DealMessage>();
            for (DealMessage message : localMessages) {
                if (!message.isSet(Flag.DELETED)) {
                    messages.add(message);
                }
            }
            for (DealListener l : mListeners) {
                l.listLocalDeals(account, messages.toArray(new DealMessage[0]));
            }
            for (DealListener l : mListeners) {
                l.listLocalDealsFinished(account);
            }
        }
        catch (Exception e) {
            for (DealListener l : mListeners) {
                l.listLocalDealsFailed(account, e.getMessage());
            }
        }
    }


    public void synchronizeDealList(final Account account,DealListener listener) {
        for (DealListener l : mListeners) {
            l.synchronizeDealListStarted(account);
        }
        put("synchronizeDealList", listener, new Runnable() {
            public void run() {
                //synchronizeDealListSyncronous(account);
            }
        });
    }

    class Command {
        public Runnable runnable;

        public DealListener listener;

        public String description;
    }

}
